Needs to be persistent for one uptime.
"""
import os
+import string
import threading
import xen.lowlevel.xc
+import XendDomainInfo
+
from xen.xend import sxp
from xen.xend import XendRoot
from xen.xend import XendCheckpoint
-from xen.xend.XendDomainInfo import XendDomainInfo
from xen.xend import EventServer
from xen.xend.XendError import XendError
from xen.xend.XendLogging import log
PRIV_DOMAIN = 0
-class XendDomainDict(dict):
- def get_by_name(self, name):
- try:
- return filter(lambda d: d.getName() == name, self.values())[0]
- except IndexError, err:
- return None
-
class XendDomain:
"""Index of all domains. Singleton.
"""
- """Dict of domain info indexed by domain id."""
- domains = None
-
-
## public:
def __init__(self):
# to import XendDomain from XendDomainInfo causes unbounded recursion.
# So we stuff the XendDomain instance (self) into xroot's components.
xroot.add_component("xen.xend.XendDomain", self)
- self.domains = XendDomainDict()
- self.refresh_lock = threading.Condition()
+ self.domains = {}
+ self.domains_lock = threading.Condition()
self.watchReleaseDomain()
- self.refresh()
- self.dom0_setup()
+
+ self.domains_lock.acquire()
+ try:
+ self.refresh()
+ self.dom0_setup()
+ finally:
+ self.domains_lock.release()
+
def list(self):
"""Get list of domain objects.
@return: domain objects
"""
- self.refresh()
- return self.domains.values()
+ self.domains_lock.acquire()
+ try:
+ self.refresh()
+ return self.domains.values()
+ finally:
+ self.domains_lock.release()
+
def list_sorted(self):
"""Get list of domain objects, sorted by name.
## private:
def onReleaseDomain(self):
- self.refresh()
+ self.domains_lock.acquire()
+ try:
+ self.refresh()
+ finally:
+ self.domains_lock.release()
+
def watchReleaseDomain(self):
from xen.xend.xenstore.xswatch import xswatch
return dominfo
- def recreate_domain(self, xeninfo):
- """Refresh initial domain info from db."""
-
- dominfo = XendDomainInfo.recreate(xeninfo)
- self._add_domain(dominfo)
- return dominfo
-
-
def dom0_setup(self):
- dom0 = self.domain_lookup(PRIV_DOMAIN)
+ dom0 = self.domains[PRIV_DOMAIN]
dom0.dom0_enforce_vcpus()
def refresh(self):
"""Refresh domain list from Xen.
"""
- self.refresh_lock.acquire()
- try:
- doms = self.xen_domains()
- for d in self.domains.values():
- info = doms.get(d.getDomid())
- if info:
- d.update(info)
- else:
- self._delete_domain(d.getDomid())
- for d in doms:
- if d not in self.domains and not doms[d]['dying']:
- try:
- self.recreate_domain(doms[d])
- except:
- if d == PRIV_DOMAIN:
- log.exception(
- "Failed to recreate information for domain "
- "%d. Doing nothing except crossing my "
- "fingers.", d)
- else:
- log.exception(
- "Failed to recreate information for domain "
- "%d. Destroying it in the hope of "
- "recovery.", d)
- try:
- xc.domain_destroy(dom = d)
- except:
- log.exception('Destruction of %d failed.', d)
- finally:
- self.refresh_lock.release()
-
-
- def update_domain(self, id):
- """Update information for a single domain.
-
- @param id: domain id
- """
- dominfo = self.xen_domain(id)
- if dominfo:
- d = self.domains.get(id)
- if d:
- d.update(dominfo)
- else:
- self._delete_domain(id)
+ doms = self.xen_domains()
+ for d in self.domains.values():
+ info = doms.get(d.getDomid())
+ if info:
+ d.update(info)
+ else:
+ self._delete_domain(d.getDomid())
+ for d in doms:
+ if d not in self.domains and not doms[d]['dying']:
+ try:
+ dominfo = XendDomainInfo.recreate(doms[d])
+ self._add_domain(dominfo)
+ except:
+ if d == PRIV_DOMAIN:
+ log.exception(
+ "Failed to recreate information for domain "
+ "%d. Doing nothing except crossing my "
+ "fingers.", d)
+ else:
+ log.exception(
+ "Failed to recreate information for domain "
+ "%d. Destroying it in the hope of "
+ "recovery.", d)
+ try:
+ xc.domain_destroy(dom = d)
+ except:
+ log.exception('Destruction of %d failed.', d)
## public:
@param config: configuration
@return: domain
"""
- dominfo = XendDomainInfo.create(config)
- self._add_domain(dominfo)
- return dominfo
+ self.domains_lock.acquire()
+ try:
+ dominfo = XendDomainInfo.create(config)
+ self._add_domain(dominfo)
+ return dominfo
+ finally:
+ self.domains_lock.release()
+
def domain_configure(self, config):
"""Configure an existing domain.
raise XendError("can't read guest state file %s: %s" %
(src, ex[1]))
- def domain_get(self, id):
- """Get up-to-date info about a domain.
- @param id: domain id
- @return: domain object (or None)
- """
- self.update_domain(id)
- return self.domains.get(id)
+ def domain_lookup(self, id):
+ self.domains_lock.acquire()
+ try:
+ self.refresh()
+ return self.domains.get(id)
+ finally:
+ self.domains_lock.release()
- def domain_lookup(self, id):
- self.refresh()
- return self.domains.get(id)
+ def domain_lookup_nr(self, id):
+ self.domains_lock.acquire()
+ try:
+ return self.domains.get(id)
+ finally:
+ self.domains_lock.release()
- def domain_lookup_by_name(self, name):
- dominfo = self.domains.get_by_name(name)
- if not dominfo:
- try:
- id = int(name)
- dominfo = self.domain_lookup(id)
- except ValueError:
- pass
- return dominfo
+
+ def domain_lookup_by_name_or_id(self, name):
+ self.domains_lock.acquire()
+ try:
+ self.refresh()
+ return self.domain_lookup_by_name_or_id_nr(name)
+ finally:
+ self.domains_lock.release()
+
+
+ def domain_lookup_by_name_or_id_nr(self, name):
+ self.domains_lock.acquire()
+ try:
+ dominfo = self.domain_lookup_by_name_nr(name)
+
+ if dominfo:
+ return dominfo
+ else:
+ try:
+ return self.domains.get(int(name))
+ except ValueError:
+ return None
+ finally:
+ self.domains_lock.release()
+
+
+ def domain_lookup_by_name_nr(self, name):
+ self.domains_lock.acquire()
+ try:
+ matching = filter(lambda d: d.getName() == name,
+ self.domains.values())
+ n = len(matching)
+ if n == 1:
+ return matching[0]
+ elif n > 1:
+ raise XendError(
+ 'Name uniqueness has been violated for name %s' % name)
+ else:
+ return None
+ finally:
+ self.domains_lock.release()
def privilegedDomain(self):
- return self.domains[PRIV_DOMAIN]
+ self.domains_lock.acquire()
+ try:
+ return self.domains[PRIV_DOMAIN]
+ finally:
+ self.domains_lock.release()
def domain_unpause(self, id):
@param reason: shutdown reason: poweroff, reboot, suspend, halt
"""
- self.callInfo(domid, XendDomainInfo.shutdown, reason)
+ self.callInfo(domid, XendDomainInfo.XendDomainInfo.shutdown, reason)
def domain_sysrq(self, domid, key):
"""Send a SysRq to the specified domain."""
- return self.callInfo(domid, XendDomainInfo.send_sysrq, key)
+ return self.callInfo(domid, XendDomainInfo.XendDomainInfo.send_sysrq,
+ key)
def domain_destroy(self, domid):
def domain_device_create(self, domid, devconfig):
"""Create a new device for the specified domain.
"""
- return self.callInfo(domid, XendDomainInfo.device_create, devconfig)
+ return self.callInfo(domid,
+ XendDomainInfo.XendDomainInfo.device_create,
+ devconfig)
def domain_device_configure(self, domid, devconfig, devid):
"""Configure an existing device in the specified domain.
@return: updated device configuration
"""
- return self.callInfo(domid, XendDomainInfo.device_configure,
+ return self.callInfo(domid,
+ XendDomainInfo.XendDomainInfo.device_configure,
devconfig, devid)
def domain_device_refresh(self, domid, devtype, devid):
"""Refresh a device."""
- return self.callInfo(domid, XendDomainInfo.device_refresh, devtype,
- devid)
+ return self.callInfo(domid,
+ XendDomainInfo.XendDomainInfo.device_refresh,
+ devtype, devid)
def domain_device_destroy(self, domid, devtype, devid):
"""Destroy a device."""
- return self.callInfo(domid, XendDomainInfo.destroyDevice, devtype,
- devid)
+ return self.callInfo(domid,
+ XendDomainInfo.XendDomainInfo.destroyDevice,
+ devtype, devid)
def domain_devtype_ls(self, domid, devtype):
"""Get list of device sxprs for the specified domain."""
- return self.callInfo(domid, XendDomainInfo.getDeviceSxprs, devtype)
+ return self.callInfo(domid,
+ XendDomainInfo.XendDomainInfo.getDeviceSxprs,
+ devtype)
def domain_vif_limit_set(self, id, vif, credit, period):
@param mem: memory target (in MiB)
"""
- self.callInfo(domid, XendDomainInfo.setMemoryTarget, mem << 10)
+ self.callInfo(domid, XendDomainInfo.XendDomainInfo.setMemoryTarget,
+ mem << 10)
def domain_vcpu_hotplug(self, domid, vcpu, state):
@param vcpu: target VCPU in domain
@param state: which state VCPU will become
"""
- self.callInfo(domid, XendDomainInfo.vcpu_hotplug, vcpu, state)
+ self.callInfo(domid, XendDomainInfo.XendDomainInfo.vcpu_hotplug, vcpu,
+ state)
def domain_dumpcore(self, domid):
"""Save a core dump for a crashed domain."""
- self.callInfo(domid, XendDomainInfo.dumpCore)
+ self.callInfo(domid, XendDomainInfo.XendDomainInfo.dumpCore)
## private:
]
+def create(config):
+ """Create a VM from a configuration.
+
+ @param config configuration
+ @raise: VmError for invalid configuration
+ """
+
+ log.debug("XendDomainInfo.create(%s)", config)
+
+ vm = XendDomainInfo(getUuid(), parseConfig(config))
+ vm.construct()
+ vm.refreshShutdown()
+ return vm
+
+
+def recreate(xeninfo):
+ """Create the VM object for an existing domain. The domain must not
+ be dying, as the paths in the store should already have been removed,
+ and asking us to recreate them causes problems."""
+
+ log.debug("XendDomainInfo.recreate(%s)", xeninfo)
+
+ assert not xeninfo['dying']
+
+ domid = xeninfo['dom']
+ try:
+ dompath = GetDomainPath(domid)
+ if not dompath:
+ raise XendError(
+ 'No domain path in store for existing domain %d' % domid)
+ vmpath = xstransact.Read(dompath, "vm")
+ if not vmpath:
+ raise XendError(
+ 'No vm path in store for existing domain %d' % domid)
+ uuid = xstransact.Read(vmpath, "uuid")
+ if not uuid:
+ raise XendError(
+ 'No vm/uuid path in store for existing domain %d' % domid)
+
+ log.info("Recreating domain %d, UUID %s.", domid, uuid)
+
+ vm = XendDomainInfo(uuid, xeninfo, domid, True)
+
+ except Exception, exn:
+ log.warn(str(exn))
+
+ uuid = getUuid()
+
+ log.info("Recreating domain %d with new UUID %s.", domid, uuid)
+
+ vm = XendDomainInfo(uuid, xeninfo, domid, True)
+ vm.storeVmDetails()
+ vm.storeDomDetails()
+
+ vm.create_channel()
+ if domid == 0:
+ vm.initStoreConnection()
+
+ vm.refreshShutdown(xeninfo)
+ return vm
+
+
def restore(config):
"""Create a domain and a VM object to do a restore.
except TypeError, exn:
raise VmError('Invalid ssidref in config: %s' % exn)
- vm = XendDomainInfo(uuid, XendDomainInfo.parseConfig(config),
+ vm = XendDomainInfo(uuid, parseConfig(config),
xc.domain_create(ssidref = ssidref))
vm.storeVmDetails()
vm.configure()
return vm
-def domain_exists(name):
+def parseConfig(config):
+ def get_cfg(name, conv = None):
+ val = sxp.child_value(config, name)
+
+ if conv and not val is None:
+ try:
+ return conv(val)
+ except TypeError, exn:
+ raise VmError(
+ 'Invalid setting %s = %s in configuration: %s' %
+ (name, val, str(exn)))
+ else:
+ return val
+
+
+ log.debug("parseConfig: config is %s" % str(config))
+
+ result = {}
+
+ for e in ROUNDTRIPPING_CONFIG_ENTRIES:
+ result[e[0]] = get_cfg(e[0], e[1])
+
+ result['memory'] = get_cfg('memory', int)
+ result['mem_kb'] = get_cfg('mem_kb', int)
+ result['maxmem'] = get_cfg('maxmem', int)
+ result['maxmem_kb'] = get_cfg('maxmem_kb', int)
+ result['cpu'] = get_cfg('cpu', int)
+ result['image'] = get_cfg('image')
+
+ try:
+ if result['image']:
+ result['vcpus'] = int(sxp.child_value(result['image'],
+ 'vcpus', 1))
+ else:
+ result['vcpus'] = 1
+ except TypeError, exn:
+ raise VmError(
+ 'Invalid configuration setting: vcpus = %s: %s' %
+ (sxp.child_value(result['image'], 'vcpus', 1), str(exn)))
+
+ result['backend'] = []
+ for c in sxp.children(config, 'backend'):
+ result['backend'].append(sxp.name(sxp.child0(c)))
+
+ result['device'] = []
+ for d in sxp.children(config, 'device'):
+ c = sxp.child0(d)
+ result['device'].append((sxp.name(c), c))
+
+ # Configuration option "restart" is deprecated. Parse it, but
+ # let on_xyz override it if they are present.
+ restart = get_cfg('restart')
+ if restart:
+ def handle_restart(event, val):
+ if not event in result:
+ result[event] = val
+
+ if restart == "onreboot":
+ handle_restart('on_poweroff', 'destroy')
+ handle_restart('on_reboot', 'restart')
+ handle_restart('on_crash', 'destroy')
+ elif restart == "always":
+ handle_restart('on_poweroff', 'restart')
+ handle_restart('on_reboot', 'restart')
+ handle_restart('on_crash', 'restart')
+ elif restart == "never":
+ handle_restart('on_poweroff', 'destroy')
+ handle_restart('on_reboot', 'destroy')
+ handle_restart('on_crash', 'destroy')
+ else:
+ log.warn("Ignoring malformed and deprecated config option "
+ "restart = %s", restart)
+
+ log.debug("parseConfig: result is %s" % str(result))
+ return result
+
+
+def domain_by_name(name):
# See comment in XendDomain constructor.
xd = get_component('xen.xend.XendDomain')
- return xd.domain_lookup_by_name(name)
+ return xd.domain_lookup_by_name_nr(name)
def shutdown_reason(code):
"""Get a shutdown reason from a code.
MINIMUM_RESTART_TIME = 20
- def create(cls, config):
- """Create a VM from a configuration.
-
- @param config configuration
- @raise: VmError for invalid configuration
- """
-
- log.debug("XendDomainInfo.create(%s)", config)
-
- vm = cls(getUuid(), cls.parseConfig(config))
- vm.construct()
- vm.refreshShutdown()
- return vm
-
- create = classmethod(create)
-
-
- def recreate(cls, xeninfo):
- """Create the VM object for an existing domain. The domain must not
- be dying, as the paths in the store should already have been removed,
- and asking us to recreate them causes problems."""
-
- log.debug("XendDomainInfo.recreate(%s)", xeninfo)
-
- assert not xeninfo['dying']
-
- domid = xeninfo['dom']
- try:
- dompath = GetDomainPath(domid)
- if not dompath:
- raise XendError(
- 'No domain path in store for existing domain %d' % domid)
- vmpath = xstransact.Read(dompath, "vm")
- if not vmpath:
- raise XendError(
- 'No vm path in store for existing domain %d' % domid)
- uuid = xstransact.Read(vmpath, "uuid")
- if not uuid:
- raise XendError(
- 'No vm/uuid path in store for existing domain %d' % domid)
-
- log.info("Recreating domain %d, UUID %s.", domid, uuid)
-
- vm = cls(uuid, xeninfo, domid, True)
-
- except Exception, exn:
- log.warn(str(exn))
-
- uuid = getUuid()
-
- log.info("Recreating domain %d with new UUID %s.", domid, uuid)
-
- vm = cls(uuid, xeninfo, domid, True)
- vm.storeVmDetails()
- vm.storeDomDetails()
-
- vm.create_channel()
- if domid == 0:
- vm.initStoreConnection()
-
- vm.refreshShutdown(xeninfo)
- return vm
-
- recreate = classmethod(recreate)
-
-
- def parseConfig(cls, config):
- def get_cfg(name, conv = None):
- val = sxp.child_value(config, name)
-
- if conv and not val is None:
- try:
- return conv(val)
- except TypeError, exn:
- raise VmError(
- 'Invalid setting %s = %s in configuration: %s' %
- (name, val, str(exn)))
- else:
- return val
-
-
- log.debug("parseConfig: config is %s" % str(config))
-
- result = {}
-
- for e in ROUNDTRIPPING_CONFIG_ENTRIES:
- result[e[0]] = get_cfg(e[0], e[1])
-
- result['memory'] = get_cfg('memory', int)
- result['mem_kb'] = get_cfg('mem_kb', int)
- result['maxmem'] = get_cfg('maxmem', int)
- result['maxmem_kb'] = get_cfg('maxmem_kb', int)
- result['cpu'] = get_cfg('cpu', int)
- result['image'] = get_cfg('image')
-
- try:
- if result['image']:
- result['vcpus'] = int(sxp.child_value(result['image'],
- 'vcpus', 1))
- else:
- result['vcpus'] = 1
- except TypeError, exn:
- raise VmError(
- 'Invalid configuration setting: vcpus = %s: %s' %
- (sxp.child_value(result['image'], 'vcpus', 1), str(exn)))
-
- result['backend'] = []
- for c in sxp.children(config, 'backend'):
- result['backend'].append(sxp.name(sxp.child0(c)))
-
- result['device'] = []
- for d in sxp.children(config, 'device'):
- c = sxp.child0(d)
- result['device'].append((sxp.name(c), c))
-
- # Configuration option "restart" is deprecated. Parse it, but
- # let on_xyz override it if they are present.
- restart = get_cfg('restart')
- if restart:
- def handle_restart(event, val):
- if not event in result:
- result[event] = val
-
- if restart == "onreboot":
- handle_restart('on_poweroff', 'destroy')
- handle_restart('on_reboot', 'restart')
- handle_restart('on_crash', 'destroy')
- elif restart == "always":
- handle_restart('on_poweroff', 'restart')
- handle_restart('on_reboot', 'restart')
- handle_restart('on_crash', 'restart')
- elif restart == "never":
- handle_restart('on_poweroff', 'destroy')
- handle_restart('on_reboot', 'destroy')
- handle_restart('on_crash', 'destroy')
- else:
- log.warn("Ignoring malformed and deprecated config option "
- "restart = %s", restart)
-
- log.debug("parseConfig: result is %s" % str(result))
- return result
-
-
- parseConfig = classmethod(parseConfig)
-
-
def __init__(self, uuid, info, domid = None, augment = False):
self.uuid = uuid
if c in '_-.:/+': continue
if c in string.ascii_letters: continue
raise VmError('invalid vm name')
- dominfo = domain_exists(name)
- # When creating or rebooting, a domain with my name should not exist.
- # When restoring, a domain with my name will exist, but it should have
- # my domain id.
+
+ dominfo = domain_by_name(name)
if not dominfo:
return
if dominfo.is_terminated():
backdom_name = sxp.child_value(config, 'backend')
if backdom_name:
- backdom = xd.domain_lookup_by_name(backdom_name)
+ backdom = xd.domain_lookup_by_name_or_id_nr(backdom_name)
else:
backdom = xd.privilegedDomain()
self.xd = XendDomain.instance()
def domain(self, x):
- dom = self.xd.domain_lookup_by_name(x)
+ dom = self.xd.domain_lookup_by_name_or_id(x)
if not dom:
raise XendError('No such domain ' + str(x))
return SrvDomain(dom)